/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */
package com.pig4cloud.pigx.contract.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.customize.enums.CommonDictEnum;
import com.pig4cloud.pigx.common.customize.i18n.util.MessageUtils;
import com.pig4cloud.pigx.common.customize.mongo.utils.MongoUtils;
import com.pig4cloud.pigx.common.customize.security.utils.SecurityUtil;
import com.pig4cloud.pigx.common.customize.utils.id.IdWorker;
import com.pig4cloud.pigx.common.security.service.PigxUser;
import com.pig4cloud.pigx.contract.api.feign.ContractMarketRecordFeign;
import com.pig4cloud.pigx.contract.constant.I18nMessages;
import com.pig4cloud.pigx.contract.entity.ContractOptionalSymbol;
import com.pig4cloud.pigx.contract.entity.ContractSymbol;
import com.pig4cloud.pigx.contract.mapper.ContractOptionalSymbolMapper;
import com.pig4cloud.pigx.contract.mapper.ContractSymbolMapper;
import com.pig4cloud.pigx.contract.mapstruct.ContractSymbolStruct;
import com.pig4cloud.pigx.contract.mongo.entity.ContractSymbolHierarchyMongo;
import com.pig4cloud.pigx.contract.mongo.entity.ContractSymbolMongo;
import com.pig4cloud.pigx.contract.service.ContractSymbolService;
import com.pig4cloud.pigx.contract.utils.ContractUtils;
import com.pig4cloud.pigx.contract.vo.AppContractSymbolDetailVO;
import com.pig4cloud.pigx.contract.vo.ContractAppSymbolDetailedVO;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 币对
 *
 * @since 2022-08-09 20:29:23
 */
@AllArgsConstructor
@Service
public class ContractSymbolServiceImpl extends ServiceImpl<ContractSymbolMapper, ContractSymbol> implements ContractSymbolService {

    private final MongoTemplate mongo;

    private final IdWorker idWorker;
    private final ContractUtils contractUtils;

    private final ContractMarketRecordFeign feign;

    private final ContractOptionalSymbolMapper optionalSymbolMapper;

    private final RedisTemplate redis;

    /**
     * 新增币对
     *
     * @param symbol 币对信息
     * @return R
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R saveSymbol(ContractSymbol symbol) {
        try {
            symbol.setId(idWorker.nextId());
            // 获取币对列表 默认设置主币 USDT
            R r = feign.contractList(SecurityConstants.FROM_IN);
            List<Map<String, String>> data = (List<Map<String, String>>) r.getData();
            data.forEach(e -> {
                if (e.get("name") != null && "1".equals(e.get("name"))) {
                    symbol.setMainCurId(Long.valueOf(e.get("id")));
                }
            });
            if (symbol.getMainCurId() == null) {
                return R.failed("主币获取错误,请联系管理员!");
            }
            if (symbol.getMainCurId().equals(symbol.getTokenCurId())) {
                return R.failed("主币和外币不能为同一种货币!");
            }
            // 校重 主外币具有唯一关联性质
            QueryWrapper<ContractSymbol> wrapper = new QueryWrapper<>();
            wrapper.eq("main_cur_id", symbol.getMainCurId());
            wrapper.eq("token_cur_id", symbol.getTokenCurId());
            wrapper.eq("is_deleted", 0);
            Long count = baseMapper.selectCount(wrapper);
            if (count > 0) {
                return R.failed("币对重复,请勿重复添加!");
            }
            // 默认停用
            symbol.setIsDeleted(CommonDictEnum.IsDeleted.NO.getValue());
            symbol.setStatus(CommonDictEnum.Status.DEPRECATED.getValue());
            // mysql 新增
            baseMapper.insert(symbol);
            // mongoDB 同步新增
            ContractSymbolMongo mongo = new ContractSymbolMongo();
            // 实体之间字段逐一拷贝赋值
            BeanUtils.copyProperties(symbol, mongo);
            this.mongo.save(mongo);
            return R.ok("添加币对成功!");
        } catch (Exception e) {
            e.printStackTrace();
            return R.failed("添加币对失败!");
        }
    }


    /**
     * 编辑（修改） 币对
     *
     * @param symbol 币对信息
     * @return R
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R updateSymbol(ContractSymbol symbol) {
        try {
            symbol.setIsDeleted("0");
            // 检查此次更新数据时没数据是否正常
            QueryWrapper<ContractSymbol> wrapper = new QueryWrapper<>();
            wrapper.eq("token_cur_id", symbol.getTokenCurId());
            wrapper.ne("id", symbol.getId());
            wrapper.eq("is_deleted", 0);
            Long count = baseMapper.selectCount(wrapper);
            if (count > 0) {
                return R.failed("币对重复,请勿重复添加!");
            }
            // mysql 修改
            baseMapper.updateById(symbol);
            // mongoDB 同步修改
            ContractSymbolMongo mongo = new ContractSymbolMongo();
            // 实体之间字段逐一拷贝赋值
            BeanUtils.copyProperties(symbol, mongo);
            // 修改条件
            Query query = Query.query(Criteria.where("id").is(mongo.getId()));
            // 修改内容
            Update update = MongoUtils.getUpdate(mongo);
            this.mongo.updateMulti(query, update, ContractSymbolMongo.class);
            return R.ok("编辑币对成功!");
        } catch (Exception e) {
            e.printStackTrace();
            return R.failed("编辑币对失败!");
        }
    }

    /**
     * 逻辑删除币对
     *
     * @param id 主键id
     * @return R
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R deleteSymbol(Long id) {
        try {
            ContractSymbol symbol = new ContractSymbol();
            symbol.setIsDeleted(null);
            symbol.setId(id);
            // 逻辑删除字段已做忽略判断处理，此时修改将直接把isDeleted置为null
            baseMapper.updateById(symbol);
            // mongoDB执行删除
            Query query = Query.query(Criteria.where("id").is(id));
            mongo.remove(query, ContractSymbolMongo.class);
            return R.ok("删除成功");
        } catch (Exception e) {
            e.printStackTrace();
            return R.failed("删除失败");
        }
    }

    /**
     * app端 根据id查询详情
     *
     * @param id id
     * @return {@link AppContractSymbolDetailVO}
     * @since 2022/8/24 22:23
     */
    @Override
    public AppContractSymbolDetailVO appDetail(Long id) {
        PigxUser user = SecurityUtil.getLoginUserNotNull();
        // 查询币对信息
        ContractSymbolMongo symbol = contractUtils.getSymbolById(id);
        // 转换对象
        AppContractSymbolDetailVO res = ContractSymbolStruct.INSTANCE.mongo2DetailVO(symbol);
        // 查询已存在仓位信息
        res.setExistPositionInfoBO(contractUtils.getExistPositionInfo(user.getId(), id));
        res.setLevel(getLevelFromRedis(res.getName()));
        return res;
    }

    private List<String> getLevelFromRedis(String symbol) {
        try {
            String key = "depth:" + symbol + ":levellist";
            List<String> list = redis.opsForList().range(key, 0, -1);
            assert list != null;
            list.sort(Comparator.naturalOrder());
            return list;
        } catch (Exception ex) {
            throw ex;
        }
    }

    @Override
    public ContractAppSymbolDetailedVO getDetailed(Long id) {
        Long userId = SecurityUtil.getLoginUserNotNull().getId();

        // 查询币对信息
        Query querySymbol = Query.query(Criteria.where("id").is(id));
        ContractSymbolMongo symbol = mongo.findOne(querySymbol, ContractSymbolMongo.class);
        if (Objects.isNull(symbol)) {
            throw new RuntimeException(MessageUtils.get(I18nMessages.FAILED_TO_OBTAIN_COIN_PAIR_INFORMATION));
        }

        ContractAppSymbolDetailedVO vo = new ContractAppSymbolDetailedVO();
        BeanUtils.copyProperties(symbol, vo);

        // 查询是否是自选
        QueryWrapper<ContractOptionalSymbol> queryOptionalCount = new QueryWrapper<>();
        queryOptionalCount.lambda()
                .eq(ContractOptionalSymbol::getUserId, userId)
                .eq(ContractOptionalSymbol::getSymbolId, symbol.getId());
        boolean exists = optionalSymbolMapper.exists(queryOptionalCount);
        vo.setIsOptional(exists);

        try {
            // 查询头寸信息
            List<ContractSymbolHierarchyMongo> hierarchyList = mongo.find(Query.query(Criteria.where("symbolId").is(id)), ContractSymbolHierarchyMongo.class);
            vo.setSymbolHierarchy(hierarchyList);
        } catch (Exception ignored) {
            vo.setSymbolHierarchy(Collections.emptyList());
        }

        return vo;
    }
}
